package com.xiaoyu.tio.redis.core.client;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import com.xiaoyu.tio.redis.core.CurrencyCommand;
import com.xiaoyu.tio.redis.core.MessageResponse;
import com.xiaoyu.tio.redis.core.RedisCmd;
import com.xiaoyu.tio.redis.core.Tedis;
import com.xiaoyu.tio.redis.core.exception.TedisException;
import com.xiaoyu.tio.redis.core.protocol.TedisCodec;
import com.xiaoyu.tio.redis.core.util.TimeoutUtils;

public class TedisStandaloneClient<K, V> implements CurrencyCommand<K, V>{
	
	private Tedis<K, V> tedis;
	
	private TedisCodec<?, K, V> codec;

	@SuppressWarnings("unchecked")
	public TedisStandaloneClient(Tedis<K, V> tedis){
		this.tedis = tedis;
		codec = tedis.getTedisCodec();
	}
	
	public void set(K key, V value) {
		try {
			tedis.sendCommand(RedisCmd.SET, codec.encoderKey(key), codec.encoderValue(value));
		} catch (TimeoutException e) {
			throw new TedisException(e);
		}
	}

	
	
	public void set(K key, V value, long timeout, TimeUnit unit) {	
		long t = TimeoutUtils.toMillis(timeout, unit);
		try {
			tedis.sendCommand(RedisCmd.SET, codec.encoderKey(key), codec.encoderValue(value), SetOption.PX.name().getBytes(), String.valueOf(t).getBytes());
		} catch (TimeoutException e) {
			throw new TedisException(e);
		}
		
	}
	
	
	public Boolean setIfAbsent(K key, V value) {
		try {
			MessageResponse messageResponse = tedis.sendCommand(RedisCmd.SETNX, codec.encoderKey(key), codec.encoderValue(value));
			byte[] v = null;
			if(messageResponse.getData().size() == 1 && (v = messageResponse.getData().get(0)) != null) {
				String f = String.valueOf(v);
				if("1".equals(f)) {
					return true;
				}else {
					return false;
				}
			}
			return null;
		} catch (TimeoutException e) {
			throw new TedisException(e);
		}
	}
	
	public V get(K key) {
		try {
			MessageResponse messageResponse = tedis.sendCommand(RedisCmd.GET, codec.encoderKey(key));
			byte[] v = null;
			if(messageResponse.getData().size() == 1 && (v = messageResponse.getData().get(0)) != null) {
				return codec.decoderValue(v);
			}
			return null;
		} catch (TimeoutException e) {
			throw new TedisException(e);
		}

	}



	@Override
	public V getAndSet(K key, V value) {
		byte[] v = null;
		try {
			MessageResponse messageResponse = tedis.sendCommand(RedisCmd.GETSET, codec.encoderKey(key), codec.encoderValue(value));
			if(messageResponse.getData().size() == 1 && (v = messageResponse.getData().get(0)) != null) {
				return codec.decoderValue(v);
			}
			return null;
		} catch (TimeoutException e) {
			throw new TedisException(e);
		}
	}



	@Override
	public Long increment(K key, long delta) {
		byte[] v = null;
		try {
			MessageResponse messageResponse = tedis.sendCommand(RedisCmd.INCRBYFLOAT, codec.encoderKey(key), String.valueOf(delta).getBytes());		
			if(messageResponse.getData().size() == 1 && (v = messageResponse.getData().get(0)) != null) {			
				return Long.valueOf(new String(v));
			}
			return null;
		} catch (TimeoutException e) {
			throw new TedisException(e);
		} catch (NumberFormatException e) {
			throw new TedisException(new String(v));
		}
	}



	@Override
	public Double increment(K key, double delta) {
		byte[] v = null;
		try {
			MessageResponse messageResponse = tedis.sendCommand(RedisCmd.INCRBYFLOAT, codec.encoderKey(key), String.valueOf(delta).getBytes());
			if(messageResponse.getData().size() == 1 && (v = messageResponse.getData().get(0)) != null) {			
				return Double.valueOf(new String(v));
			}
			return null;
		} catch (TimeoutException e) {
			throw new TedisException(e);
		} catch (NumberFormatException e) {
			throw new TedisException(new String(v));
		}
	}



	@Override
	public Integer append(K key, String value) {
		byte[] v = null;
		try {
			MessageResponse messageResponse = tedis.sendCommand(RedisCmd.APPEND, codec.encoderKey(key), value.getBytes());
			if(messageResponse.getData().size() == 1 && (v = messageResponse.getData().get(0)) != null) {			
				return Integer.valueOf(String.valueOf(v));
			}
			return null;
		} catch (TimeoutException e) {
			throw new TedisException(e);
		} catch (NumberFormatException e) {
			throw new TedisException(new String(v));
		}
	}



	@Override
	public String get(K key, long start, long end) {
		try {
			MessageResponse messageResponse = tedis.sendCommand(RedisCmd.GETRANGE, codec.encoderKey(key), String.valueOf(start).getBytes(), String.valueOf(end).getBytes());
			byte[] v = null;
			if(messageResponse.getData().size() == 1 && (v = messageResponse.getData().get(0)) != null) {			
				return String.valueOf(v);
			}
			return null;
		} catch (TimeoutException e) {
			throw new TedisException(e);
		}
	}



	@Override
	public void set(K key, V value, long offset) {
		try {
			tedis.sendCommand(RedisCmd.GETRANGE, codec.encoderKey(key), codec.encoderValue(value), String.valueOf(offset).getBytes());
		} catch (TimeoutException e) {
			throw new TedisException(e);
		}	
	}



	@Override
	public Boolean setBit(K key, long offset, boolean value) {
		try {
			MessageResponse messageResponse = tedis.sendCommand(RedisCmd.GETRANGE, codec.encoderKey(key), String.valueOf(value).getBytes(), String.valueOf(offset).getBytes());
			byte[] v = null;
			if(messageResponse.getData().size() == 1 && (v = messageResponse.getData().get(0)) != null) {		
				String f = String.valueOf(v);
				if("1".equals(f)) {
					return true;
				}else {
					return false;
				}
			}
			return null;
		} catch (TimeoutException e) {
			throw new TedisException(e);
		}
	}



	@Override
	public Boolean getBit(K key, long offset) {
		try {
			MessageResponse messageResponse = tedis.sendCommand(RedisCmd.GETRANGE, codec.encoderKey(key), String.valueOf(offset).getBytes());
			byte[] v = null;
			if(messageResponse.getData().size() == 1 && (v = messageResponse.getData().get(0)) != null) {		
				String f = String.valueOf(v);
				if("1".equals(f)) {
					return true;
				}else {
					return false;
				}
			}
			return null;
		} catch (TimeoutException e) {
			throw new TedisException(e);
		}
	}
	
}
